(module environments (lib "eopl.ss" "eopl") 
  
  (require "data-structures.scm")
  (provide init-env empty-env extend-env extend-env-rec apply-env)

;;;;;;;;;;;;;;;; initial environment ;;;;;;;;;;;;;;;;
  
  ;; init-env : () -> environment

  ;; (init-env) builds an environment in which i is bound to the
  ;; expressed value 1, v is bound to the expressed value 5, and x is
  ;; bound to the expressed value 10.  

  (define init-env 
    (lambda ()
      (extend-env 
       'i (num-val 1)
       (extend-env
        'v (num-val 5)
        (extend-env
         'x (num-val 10)
         (empty-env))))))

;;;;;;;;;;;;;;;; environment constructors and observers ;;;;;;;;;;;;;;;;

;;; represent environment as an alist  ((id rhs) ...)

;;; rhs is either an expval or a list (bvar body)
;;; expval is for extend-env; the list is for extend-env-rec.

;;; this representation is designed to make the printed representation
;;; of the environment more readable.

  (define empty-env
    (lambda ()
      '()))
  
  (define empty-env? 
    (lambda (x) (null? x)))

  (define extend-env
    (lambda (sym val old-env)
      (cons (list sym val) old-env)))

  (define extend-env-rec
    (lambda (p-name b-var p-body saved-env)
      (cons 
        (list p-name b-var p-body)
        saved-env)))

  (define apply-env
    (lambda (env search-sym)
      (if (null? env) 
        (eopl:error 'apply-env "No binding for ~s" search-sym)
        (let* ((binding (car env))
               (id (list-ref binding 0))
               (expval-or-bvar (list-ref binding 1)))
          (cond
            ((not (eqv? search-sym id))
             (apply-env (cdr env) search-sym))
            ((not (symbol? expval-or-bvar))
            ;; this was built by extend-env
             expval-or-bvar)
            (else
            ;; this was built by extend-env-rec
              (let ((bvar (cadr binding))
                    (body (caddr binding)))
                (proc-val (procedure bvar body env)))))))))
            
  )